home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World 2008 September
/
PCWorld_2008-09_cd.bin
/
v cisle
/
sadanastroju
/
lightning-0.8-tb-win.xpi
/
chrome
/
calendar.jar
/
content
/
calendar
/
calendar-month-view.xml
< prev
next >
Wrap
Extensible Markup Language
|
2008-03-13
|
63KB
|
1,767 lines
<?xml version="1.0" encoding="UTF-8"?>
<!--
- ***** BEGIN LICENSE BLOCK *****
- Version: MPL 1.1/GPL 2.0/LGPL 2.1
-
- The contents of this file are subject to the Mozilla Public License Version
- 1.1 (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
- http://www.mozilla.org/MPL/
-
- Software distributed under the License is distributed on an "AS IS" basis,
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- for the specific language governing rights and limitations under the
- License.
-
- The Original Code is calendar views.
-
- The Initial Developer of the Original Code is
- Oracle Corporation
- Portions created by the Initial Developer are Copyright (C) 2005
- the Initial Developer. All Rights Reserved.
-
- Contributor(s):
- Vladimir Vukicevic <vladimir@pobox.com>
- Stefan Sitter <ssitter@googlemail.com>
- Clint Talbert <cmtalbert@myfastmail.com>
- Michael B├╝ttner <michael.buettner@sun.com>
- Philipp Kewisch <mozilla@kewis.ch>
- Markus Adrario <MarkusAdrario@web.de>
-
- Alternatively, the contents of this file may be used under the terms of
- either the GNU General Public License Version 2 or later (the "GPL"), or
- the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- in which case the provisions of the GPL or the LGPL are applicable instead
- of those above. If you wish to allow use of your version of this file only
- under the terms of either the GPL or the LGPL, and not to allow others to
- use your version of this file under the terms of the MPL, indicate your
- decision by deleting the provisions above and replace them with the notice
- and other provisions required by the GPL or the LGPL. If you do not delete
- the provisions above, a recipient may use your version of this file under
- the terms of any one of the MPL, the GPL or the LGPL.
-
- ***** END LICENSE BLOCK *****
-->
<!-- Note that this file depends on helper functions in calUtils.js-->
<bindings id="calendar-month-view-bindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl">
<binding id="calendar-month-day-box-item" extends="chrome://calendar/content/calendar-view-core.xml#calendar-editable-item">
<content>
<xul:vbox flex="1">
<xul:hbox>
<xul:image anonid="shadow-left-image" class="calendar-event-box-shadow-left"/>
<xul:box anonid="event-container" flex="1">
<xul:box class="calendar-event-selection" orient="horizontal" flex="1">
<xul:stack anonid="eventbox"
class="calendar-event-box-container"
flex="1">
<xul:hbox class="calendar-event-details">
<xul:image anonid="item-icon"
class="calendar-month-day-box-item-image"/>
<xul:label anonid="item-label"
class="calendar-month-day-box-item-label"
xbl:inherits="context"/>
<xul:vbox align="left"
flex="1"
xbl:inherits="context">
<xul:label anonid="event-name"
crop="end"
flex="1"
style="margin: 0;"/>
<xul:textbox anonid="event-name-textbox"
class="plain"
crop="end"
hidden="true"
style="background: transparent !important;"
wrap="true"/>
<xul:spacer flex="1"/>
</xul:vbox>
<xul:vbox pack="center">
<xul:image anonid="alarm-image"
class="alarm-image"
xbl:inherits="flashing"
hidden="true"/>
</xul:vbox>
</xul:hbox>
<xul:image anonid="gradient"
class="calendar-event-box-gradient"
hidden="true" height="1px"/>
</xul:stack>
</xul:box>
<xul:calendar-category-box anonid="category-box"/>
</xul:box>
<xul:image anonid="shadow-right-image" class="calendar-event-box-shadow-right"/>
</xul:hbox>
<xul:box anonid="shadow-container" orient="horizontal">
<xul:image class="calendar-event-box-shadow-edge-left"/>
<xul:image class="calendar-event-box-shadow-bottom" flex="1"/>
<xul:image class="calendar-event-box-shadow-edge-right"/>
</xul:box>
</xul:vbox>
</content>
<implementation>
<constructor><![CDATA[
var gradient = document.getAnonymousElementByAttribute(this,
"anonid",
"gradient");
var container = document.getAnonymousElementByAttribute(this,
"anonid",
"event-container");
if (gradient) {
gradient.removeAttribute("hidden");
}
container.setAttribute("class", "calendar-item");
container.setAttribute("class", this.getAttribute("class"));
container.setAttribute("item-calendar", this.getAttribute("item-calendar"));
this.removeAttribute("class");
this.removeAttribute("item-calendar");
]]>
</constructor>
<property name="occurrence">
<getter><![CDATA[
return this.mOccurrence;
]]></getter>
<setter><![CDATA[
this.mOccurrence = val;
var icon = document.getAnonymousElementByAttribute(this,"anonid","item-icon");
if (isEvent(val)) {
icon.setAttribute("type", "event");
if (val.startDate.isDate) {
icon.setAttribute("type", "all-day");
} else {
var label = document.getAnonymousElementByAttribute(this,"anonid","item-label");
var df = Components.classes["@mozilla.org/calendar/datetime-formatter;1"].
getService(Components.interfaces.calIDateTimeFormatter);
var timezone = this.calendarView? this.calendarView.mTimezone:
calendarDefaultTimezone();
label.value = df.formatTime(
val.startDate.getInTimezone(timezone));
label.setAttribute("time", "true");
}
} else if (isToDo(val)) {
icon.setAttribute("type", "todo");
if (val.isCompleted) {
icon.setAttribute("completed", "true");
}
}
this.setEditableLabel();
this.setCSSClasses();
return val;
]]></setter>
</property>
<property name="parentBox"
onget="return this.mParentBox;"
onset="this.mParentBox = val;"/>
<method name="removeShadows">
<body><![CDATA[
removeAnonymousElement(this, "shadow-left-image");
removeAnonymousElement(this, "shadow-right-image");
removeAnonymousElement(this, "shadow-container");
]]></body>
</method>
</implementation>
<handlers>
<handler event="draggesture"><![CDATA[
var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].
getService(Components.interfaces.nsIDragService);
var transfer = Components.classes["@mozilla.org/widget/transferable;1"].
createInstance(Components.interfaces.nsITransferable);
transfer.addDataFlavor("text/calendar");
var item = this.occurrence;
if (!isCalendarWritable(item.calendar)) {
return;
}
var flavourProvider = {
QueryInterface: function(aIID) {
ensureIID(
[ Components.interfaces.nsIFlavorDataProvider,
Components.interfaces.nsISupports], aIID);
return this;
},
item: item,
getFlavorData: function(aInTransferable, aInFlavor, aOutData, aOutDataLen) {
if ((aInFlavor == "application/vnd.x-moz-cal-event") ||
(aInFlavor == "application/vnd.x-moz-cal-task")) {
aOutData.value = this.item;
aOutDataLen.value = 1;
} else {
ASSERT(false, "error:"+aInFlavor);
}
}
};
if (isEvent(item)) {
transfer.addDataFlavor("application/vnd.x-moz-cal-event");
transfer.setTransferData("application/vnd.x-moz-cal-event", flavourProvider, 0);
} else if (isToDo(item)) {
transfer.addDataFlavor("application/vnd.x-moz-cal-task");
transfer.setTransferData("application/vnd.x-moz-cal-task", flavourProvider, 0);
}
// Also set some normal data-types, in case we drag into another app
var serializer = Components.classes["@mozilla.org/calendar/ics-serializer;1"].
createInstance(Components.interfaces.calIIcsSerializer);
serializer.addItems([item], 1);
var supportsString = Components.classes["@mozilla.org/supports-string;1"].
createInstance(Components.interfaces.nsISupportsString);
supportsString.data = serializer.serializeToString();
transfer.setTransferData("text/calendar", supportsString, supportsString.data.length*2);
transfer.setTransferData("text/unicode", supportsString, supportsString.data.length*2);
var action = dragService.DRAGDROP_ACTION_MOVE;
var supArray = Components.classes["@mozilla.org/supports-array;1"].
createInstance(Components.interfaces.nsISupportsArray);
supArray.AppendElement(transfer);
// OK, now that the data is all set up, start playing with the shadows
this.parentBox.monthView.doDeleteItem(item);
// Figure out how many shadows we're going to need, and how they should
// be oriented from the mouse's position
var boxes = this.parentBox.monthView.findBoxesForItem(item);
this.parentBox.monthView.mShadowLength = boxes.length;
for (var i in boxes) {
if (boxes[i].box == this.parentBox) {
this.parentBox.monthView.mShadowIndex = i;
break;
}
}
this.parentBox.addDropShadows();
// Mozilla doesn't provide any generic way for us to listen for the
// drag-end. Add this listener for a best approximation of listening
// for that case.
var monthbox = this.parentBox;
monthbox.monthView.dropListener = function checkStillDragging() {
var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].
getService(Components.interfaces.nsIDragService);
var session;
try {
session = dragService.getCurrentSession();
} catch (ex) {}
if (!session) {
monthbox.removeDropShadows();
monthbox.addItem(item);
window.removeEventListener("mouseover",
monthbox.monthView.dropListener,
true);
}
};
window.addEventListener("mouseover",
monthbox.monthView.dropListener, true);
dragService.invokeDragSession(this, supArray, null, action);
]]></handler>
</handlers>
</binding>
<binding id="calendar-month-day-box">
<content>
<xul:vbox flex="1">
<xul:label anonid="day-label" crop="end" class="calendar-month-day-box-date-label"/>
<xul:vbox anonid="day-items"/>
</xul:vbox>
</content>
<implementation>
<field name="mDate">null</field>
<!-- mItemData will always be kept sorted in display order -->
<field name="mItemData">[]</field>
<field name="mMonthView">null</field>
<field name="mShowMonthLabel">false</field>
<property name="date">
<getter>return this.mDate;</getter>
<setter>this.setDate(val); return val;</setter>
</property>
<property name="monthView">
<getter><![CDATA[
return this.mMonthView;
]]></getter>
<setter><![CDATA[
this.mMonthView = val;
return val;
]]></setter>
</property>
<property name="selected">
<getter><![CDATA[
var sel = this.getAttribute("selected");
if (sel && sel == "true") {
return true;
}
return false;
]]></getter>
<setter><![CDATA[
if (val)
this.setAttribute("selected", "true");
else
this.removeAttribute("selected");
return val;
]]></setter>
</property>
<property name="dayitems">
<getter>return document.getAnonymousElementByAttribute(this, "anonid", "day-items");</getter>
</property>
<property name="showMonthLabel">
<getter><![CDATA[
return this.mShowMonthLabel;
]]></getter>
<setter><![CDATA[
if (this.mShowMonthLabel == val) {
return val;
}
this.mShowMonthLabel = val;
if (!this.mDate) {
return val;
}
var daylabel = document.getAnonymousElementByAttribute(this, "anonid", "day-label");
if (val) {
var monthName = calGetString("dateFormat", "month." + (this.mDate.month+1) + ".Mmm");
daylabel.setAttribute("value", this.mDate.day + " " + monthName);
} else {
daylabel.setAttribute("value", this.mDate.day);
}
return val;
]]></setter>
</property>
<method name="setDate">
<parameter name="aDate"/>
<body><![CDATA[
if (!aDate)
throw Components.results.NS_ERROR_NULL_POINTER;
// Remove all the old events
this.mItemData = new Array();
while(this.dayitems.hasChildNodes()) {
this.dayitems.removeChild(this.dayitems.lastChild);
}
if (this.mDate && this.mDate.compare(aDate) == 0)
return;
this.mDate = aDate;
var daylabel = document.getAnonymousElementByAttribute(this, "anonid", "day-label");
if (this.mShowMonthLabel)
{
var monthName = calGetString("dateFormat", "month." + (aDate.month+1) + ".Mmm");
daylabel.setAttribute("value", aDate.day + " " + monthName);
} else {
daylabel.setAttribute("value", aDate.day);
}
]]></body>
</method>
<method name="addItem">
<parameter name="aItem"/>
<body><![CDATA[
for each (ed in this.mItemData) {
if (aItem.hashId == ed.item.hashId)
{
this.deleteItem(aItem);
break;
}
}
function comptor(a, b) {
var aIsEvent = isEvent(a.item);
var aIsTodo = isToDo(a.item);
var bIsEvent = isEvent(b.item);
var bIsTodo = isToDo(b.item);
if ((!aIsEvent && !aIsTodo) || (!bIsEvent && !bIsTodo)) {
// XXX ????
dump ("Don't know how to sort these two events: " + a.item + " " + b.item + "\n");
return 0;
}
// sort todos before events
if (aIsTodo && bIsEvent) return -1;
if (aIsEvent && bIsTodo) return 1;
// XXX how do I sort todos?
if (aIsTodo && bIsTodo) {
return 0;
}
if (aIsEvent && bIsEvent) {
// sort all day events before events with a duration
if (a.item.startDate.isDate && !b.item.startDate.isDate) return -1;
if (!a.item.startDate.isDate && b.item.startDate.isDate) return 1;
var cmp;
cmp = a.item.startDate.compare(b.item.startDate);
if (cmp != 0)
return cmp;
cmp = a.item.endDate.compare(b.item.endDate);
if (cmp != 0)
return cmp;
if (a.item.title < b.item.title)
return -1;
if (a.item.title > b.item.title)
return 1;
}
return 0;
}
// insert the new item block (while keeping the array sorted),
// and then relayout.
// Note: don't use Array.Sort, because that's quicksort, which
// is not good for an already mostly-sorted array
var newItem = {item: aItem};
for (i = 0; i<this.mItemData.length; ++i) {
if (comptor(this.mItemData[i], newItem) > 0)
break;
}
this.mItemData.splice(i, 0, newItem);
this.relayout();
]]></body>
</method>
<method name="selectItem">
<parameter name="aItem"/>
<body><![CDATA[
for each (var itd in this.mItemData) {
if (aItem && (itd.item.hashId == aItem.hashId)) {
itd.box.selected = true;
}
}
]]></body>
</method>
<method name="unselectItem">
<parameter name="aItem"/>
<body><![CDATA[
for each (var itd in this.mItemData) {
if (aItem && (itd.item.hashId == aItem.hashId)) {
itd.box.selected = false;
}
}
]]></body>
</method>
<method name="deleteItem">
<parameter name="aItem"/>
<body><![CDATA[
var deleted = [];
var origLen = this.mItemData.length;
this.mItemData = this.mItemData.filter(
function(itd) {
if (aItem.hashId == itd.item.hashId)
{
deleted.push(itd);
return false;
}
return true;
});
if (deleted.length > 0) {
for each (itd in deleted) {
if (itd.box)
this.dayitems.removeChild(itd.box);
}
// no need to relayout; all we did was delete
//this.relayout();
}
]]></body>
</method>
<method name="relayout">
<body><![CDATA[
function createXULElement(el) {
return document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", el);
}
for (var i = 0; i < this.mItemData.length; i++) {
var itd = this.mItemData[i];
if (!itd.box) {
// find what element to insert before
var before = null;
for (var j = i+1; !before && this.mItemData[j]; j++)
before = this.mItemData[j].box;
var box = createXULElement("calendar-month-day-box-item");
box.setAttribute("context", this.getAttribute("item-context") || this.getAttribute("context"));
box.setAttribute("class", "calendar-item");
box.setAttribute("item-calendar", itd.item.calendar.uri.spec);
box.setAttribute("tooltip", "itemTooltip");
if (itd.item.hashId in this.monthView.mFlashingEvents) {
box.setAttribute("flashing", "true");
}
var categoriesSelectorList = "";
var categoriesProperty = itd.item.getProperty("CATEGORIES");
if (categoriesProperty) {
var categoriesArray = categoriesStringToArray(categoriesProperty);
var cssClassesArray = categoriesArray.map(formatStringForCSSRule);
categoriesSelectorList = cssClassesArray.join(" ");
}
box.setAttribute("item-category", categoriesSelectorList);
this.dayitems.insertBefore(box, before);
box.calendarView = this.monthView;
box.item = itd.item;
box.occurrence = itd.item;
box.parentBox = this;
itd.box = box;
}
}
]]></body>
</method>
<!-- While you might expect 'dragexit' to be fired when we drag outside
- the node in question, this isn't actually the case. So, we need
- to keep track of these shadows on the month-view itself, so they can
- be properly removed.
-->
<method name="addDropShadows">
<body><![CDATA[
// Only allow one set of drop-boxes
if (this.monthView.mDropShadows) {
return;
}
this.monthView.mDropShadows = [];
var shadowStart = this.mDate.clone();
shadowStart.day -= this.monthView.mShadowIndex;
for (var i = 0; i < this.monthView.mShadowLength; i++) {
var dropbox = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "box");
dropbox.setAttribute("dropbox", "true");
dropbox.setAttribute("flex", "1");
var box = this.monthView.findBoxForDate(shadowStart);
if (!box) {
// Dragging to the end of a view
shadowStart.day += 1;
continue;
}
box.box.dayitems.insertBefore(dropbox, box.box.dayitems.firstChild);
this.monthView.mDropShadows.push(dropbox);
shadowStart.day += 1;
}
]]></body>
</method>
<method name="removeDropShadows">
<body><![CDATA[
if (!this.monthView.mDropShadows) {
return;
}
for each (shadow in this.monthView.mDropShadows) {
shadow.parentNode.removeChild(shadow);
}
this.monthView.mDropShadows = null;
]]></body>
</method>
</implementation>
<handlers>
<handler event="mousedown"><![CDATA[
event.stopPropagation();
if (this.mDate)
this.monthView.selectedDay = this.mDate;
]]></handler>
<handler event="dblclick"><![CDATA[
event.stopPropagation();
this.monthView.controller.createNewEvent();
]]></handler>
<handler event="dragenter"><![CDATA[
var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].
getService(Components.interfaces.nsIDragService);
var session = dragService.getCurrentSession();
if (session) {
session.canDrop = true;
this.removeDropShadows();
this.addDropShadows();
}
]]></handler>
<handler event="dragover"><![CDATA[
var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].
getService(Components.interfaces.nsIDragService);
var session = dragService.getCurrentSession();
session.canDrop = true;
]]></handler>
<handler event="dragexit"><![CDATA[
if (event.originalTarget != this) {
return;
}
this.removeDropShadows();
var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].
getService(Components.interfaces.nsIDragService);
var session = dragService.getCurrentSession();
session.canDrop = false;
]]></handler>
<handler event="dragdrop"><![CDATA[
var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].
getService(Components.interfaces.nsIDragService);
var session = dragService.getCurrentSession();
var transfer = Components.classes["@mozilla.org/widget/transferable;1"].
createInstance(Components.interfaces.nsITransferable);
transfer.addDataFlavor("application/x-moz-cal-event");
session.getData(transfer, 0);
var flavor = {};
var data = {};
// nsITransferable sucks when it comes to trying to add extra flavors.
// This will throw NS_ERROR_FAILURE, so as a workaround, we use the
// sourceNode property and get the event that way
//transfer.getAnyTransferData(flavor, data, {});
var newStart;
var newEnd;
var boxDate = this.mDate;
if (!session.sourceNode || !session.sourceNode.occurrence) {
return;
}
event.stopPropagation();
var item = session.sourceNode.occurrence;
var beginMove = session.sourceNode.mParentBox.date;
var duration = boxDate.subtractDate(beginMove);
// Since both boxDate and beginMove are dates (note datetimes),
// subtractDate will only give us a non-zero number of hours on DST
// changes. While strictly speaking, subtractDate's behavior is
// correct, we need to move the event a discrete number of days here.
if (duration.hours == 23) {
// crossing DT/DST border (spring)
duration.hours++;
duration.normalize();
} else if (duration.hours == 1) {
// crossing DST/DT border (autumn)
duration.hours--;
}
if (isEvent(item)) {
newStart = item.startDate.clone();
newStart.addDuration(duration);
newEnd = item.endDate.clone();
newEnd.addDuration(duration);
} else if (isToDo(item)) {
if (item.entryDate) {
newStart = item.entryDate.clone();
newStart.addDuration(duration);
}
if (item.dueDate) {
newEnd = item.dueDate.clone();
newEnd.addDuration(duration);
}
}
this.removeDropShadows();
window.removeEventListener("mouseover",
this.monthView.dropListener,
true);
this.monthView.controller.modifyOccurrence(item, newStart, newEnd);
]]></handler>
<handler event="click" button="0"><![CDATA[
this.monthView.setSelectedItems(0, []);
]]></handler>
</handlers>
</binding>
<binding id="calendar-month-view-column-header">
<content>
<xul:hbox flex="1">
<xul:spacer flex="1"/>
<xul:label anonid="label" crop="right" class="calendar-month-view-column-header-label" />
<xul:spacer flex="1"/>
</xul:hbox>
</content>
<implementation>
<field name="mIndex">-1</field>
<constructor><![CDATA[
if (this.mIndex == -1) {
var attrIndex = this.getAttribute("index");
if (attrIndex)
this.index = parseInt(attrIndex);
}
]]></constructor>
<property name="index">
<getter>return this.mIndex;</getter>
<setter><![CDATA[
this.mIndex = val % 7;
var label = document.getAnonymousElementByAttribute(this, "anonid", "label");
var dayName = calGetString("dateFormat", "day." + (this.mIndex+1) + ".name");
label.setAttribute("value", dayName);
return this.mIndex;
]]></setter>
</property>
</implementation>
</binding>
<binding id="calendar-month-view">
<content>
<xul:vbox flex="1">
<xul:hbox anonid="headerbox" equalsize="always"/>
<xul:grid anonid="monthgrid" flex="1">
<xul:columns anonid="monthgridcolumns" equalsize="always">
<xul:column flex="1" class="calendar-month-view-grid-column"/>
<xul:column flex="1" class="calendar-month-view-grid-column"/>
<xul:column flex="1" class="calendar-month-view-grid-column"/>
<xul:column flex="1" class="calendar-month-view-grid-column"/>
<xul:column flex="1" class="calendar-month-view-grid-column"/>
<xul:column flex="1" class="calendar-month-view-grid-column"/>
<xul:column flex="1" class="calendar-month-view-grid-column"/>
</xul:columns>
<xul:rows anonid="monthgridrows" equalsize="always">
<xul:row flex="1" class="calendar-month-view-grid-row">
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
</xul:row>
<xul:row flex="1" class="calendar-month-view-grid-row">
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
</xul:row>
<xul:row flex="1" class="calendar-month-view-grid-row">
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
</xul:row>
<xul:row flex="1" class="calendar-month-view-grid-row">
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
</xul:row>
<xul:row flex="1" class="calendar-month-view-grid-row">
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
</xul:row>
<xul:row flex="1" class="calendar-month-view-grid-row">
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
<xul:calendar-month-day-box/>
</xul:row>
</xul:rows>
</xul:grid>
</xul:vbox>
</content>
<implementation implements="calICalendarView">
<!-- constructor -->
<constructor><![CDATA[
function createXULElement(el) {
return document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", el);
}
var headerbox = document.getAnonymousElementByAttribute(this, "anonid", "headerbox")
for (var i = 0; i < 7; i++) {
var hdr = createXULElement("calendar-month-view-column-header");
hdr.setAttribute("flex", "1");
headerbox.appendChild(hdr);
hdr.index = i;
}
this.mTimezone = floating();
var alarmService = Components.classes['@mozilla.org/calendar/alarm-service;1']
.getService(Components.interfaces.calIAlarmService);
alarmService.addObserver(this.mObserver);
]]></constructor>
<destructor><![CDATA[
if (this.mCalendar) {
this.mCalendar.removeObserver(this.mObserver);
}
var alarmService = Components.classes['@mozilla.org/calendar/alarm-service;1']
.getService(Components.interfaces.calIAlarmService);
alarmService.removeObserver(this.mObserver);
]]></destructor>
<!-- fields -->
<field name="mCalendar">null</field>
<field name="mController">null</field>
<field name="mStartDate">null</field>
<field name="mEndDate">null</field>
<field name="mDateBoxes">null</field>
<field name="mTimezone">null</field>
<field name="mSelectedItems">[]</field>
<field name="mSelectedDayBox">null</field>
<field name="mShowDaysOutsideMonth">true</field>
<field name="mTasksInView">false</field>
<field name="mShowCompleted">false</field>
<field name="mShowFullMonth">true</field>
<field name="mWeekStartOffset">0</field>
<field name="mDaysOffArray">[0,6]</field>
<field name="mDisplayDaysOff">true</field>
<field name="mDropShadows">null</field>
<field name="mShadowIndex">0</field>
<field name="mFlashingEvents">new Object()</field>
<!-- other methods -->
<method name="setAttribute">
<parameter name="aAttr"/>
<parameter name="aVal"/>
<body><![CDATA[
var needsrelayout = false;
if (aAttr == "context" || aAttr == "item-context")
needsrelayout = true;
var ret = XULElement.prototype.setAttribute.call (this, aAttr, aVal);
if (needsrelayout)
this.relayout();
return ret;
]]></body>
</method>
<!-- calICalendarView -->
<property name="supportsDisjointDates" readonly="true"
onget="return false;"/>
<property name="hasDisjointDates" readonly="true"
onget="return false;"/>
<property name="displayCalendar">
<getter><![CDATA[
return this.mCalendar;
]]></getter>
<setter><![CDATA[
if (this.mCalendar != val) {
this.mCalendar = val;
this.mCalendar.addObserver(this.mObserver);
this.refresh();
}
return val;
]]></setter>
</property>
<property name="weekStartOffset">
<getter><![CDATA[
return this.mWeekStartOffset;
]]></getter>
<setter><![CDATA[
this.mWeekStartOffset = val;
return val;
]]></setter>
</property>
<property name="displayDaysOff">
<getter><![CDATA[
return this.mDisplayDaysOff;
]]></getter>
<setter><![CDATA[
this.mDisplayDaysOff = val;
return val;
]]></setter>
</property>
<property name="daysOffArray">
<getter><![CDATA[
return this.mDaysOffArray;
]]></getter>
<setter><![CDATA[
this.mDaysOffArray = val;
return val;
]]></setter>
</property>
<property name="controller"
onget="return this.mController;"
onset="return this.mController = val;"/>
<property name="startDate" readonly="true"
onget="return this.mStartDate"/>
<property name="endDate" readonly="true">
<getter><![CDATA[
return this.mEndDate;
]]></getter>
</property>
<!-- the end date that should be used for getItems and similar queries -->
<property name="queryEndDate" readonly="true">
<getter><![CDATA[
var end = this.endDate;
if (!end)
return null;
end = end.clone();
end.day += 1;
end.isDate = true;
return end;
]]></getter>
</property>
<property name="tasksInView">
<getter><![CDATA[
return this.mTasksInView;
]]></getter>
<setter><![CDATA[
this.mTasksInView = val;
return val;
]]></setter>
</property>
<property name="showCompleted">
<getter><![CDATA[
return this.mShowCompleted;
]]></getter>
<setter><![CDATA[
this.mShowCompleted = val;
return val;
]]></setter>
</property>
<property name="showFullMonth">
<getter><![CDATA[
return this.mShowFullMonth;
]]></getter>
<setter><![CDATA[
this.mShowFullMonth = val;
return val;
]]></setter>
</property>
<method name="getSelectedItems">
<parameter name="aCount"/>
<body><![CDATA[
aCount.value = this.mSelectedItems.length;
return this.mSelectedItems;
]]></body>
</method>
<method name="setSelectedItems">
<parameter name="aCount"/>
<parameter name="aItems"/>
<parameter name="aSuppressEvent"/>
<body><![CDATA[
if (this.mSelectedItems.length) {
for each (var item in this.mSelectedItems) {
var oldboxes = this.findBoxesForItem(item);
for each (oldbox in oldboxes) {
oldbox.box.unselectItem(item);
}
}
}
this.mSelectedItems = aItems || [];
if (this.mSelectedItems.length) {
for each (var item in this.mSelectedItems) {
var newboxes = this.findBoxesForItem(item);
for each (newbox in newboxes) {
newbox.box.selectItem(item);
}
}
}
if (!aSuppressEvent) {
this.fireEvent("itemselect", this.mSelectedItems);
}
]]></body>
</method>
<property name="selectedDay">
<getter><![CDATA[
if (this.mSelectedDayBox)
return this.mSelectedDayBox.date.clone();
return null;
]]></getter>
<setter><![CDATA[
if (this.mSelectedDayBox)
this.mSelectedDayBox.box.selected = false;
var realVal = val;
if (!realVal.isDate) {
realVal = val.clone();
realVal.isDate = true;
}
var box = this.findBoxForDate(realVal);
if (box) {
box.box.selected = true;
this.mSelectedDayBox = box;
}
this.fireEvent("dayselect", realVal);
return val;
]]></setter>
</property>
<property name="timezone">
<getter><![CDATA[
return this.mTimezone;
]]></getter>
<setter><![CDATA[
this.mTimezone = val;
return val;
]]></setter>
</property>
<method name="fireEvent">
<parameter name="aEventName"/>
<parameter name="aEventDetail"/>
<body><![CDATA[
var event = document.createEvent('Events');
event.initEvent(aEventName, true, false);
event.detail = aEventDetail;
this.dispatchEvent(event);
]]></body>
</method>
<method name="showDate">
<parameter name="aDate"/>
<body><![CDATA[
aDate = aDate.getInTimezone(this.mTimezone);
// We might need to do some adjusting here to make sure we still
// display whole month even with alternative week-start days
var startDate = aDate.startOfMonth;
var endDate = aDate.endOfMonth;
if (startDate.weekday < this.mWeekStartOffset) {
// Go back one week to make sure we display this day
startDate.day -= 7;
}
if (endDate.weekday < this.mWeekStartOffset) {
// Go back one week so we don't display any extra days
endDate.day -= 7;
}
this.setDateRange(startDate, endDate);
this.selectedDay = aDate;
]]></body>
</method>
<method name="setDateRange">
<parameter name="aStartDate"/>
<parameter name="aEndDate"/>
<body><![CDATA[
if (this.mTimezone != aStartDate.timezone) {
aStartDate = aStartDate.getInTimezone(this.mTimezone);
aEndDate = aEndDate.getInTimezone(this.mTimezone);
}
this.mStartDate = aStartDate.startOfWeek;
this.mEndDate = aEndDate.endOfWeek;
this.mStartDate.day += this.mWeekStartOffset;
this.mEndDate.day += this.mWeekStartOffset;
this.refresh();
]]></body>
</method>
<method name="setDateList">
<parameter name="aCount"/>
<parameter name="aDates"/>
<body><![CDATA[
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
]]></body>
</method>
<method name="getDateList">
<parameter name="aCount"/>
<body><![CDATA[
if (!this.mStartDate || !this.mEndDate) {
aCount.value = 0;
return [];
}
var results = [];
var curDate = this.mStartDate.clone();
curDate.isDate = true;
while (curDate.compare(this.mEndDate) <= 0) {
results.push(curDate.clone());
curDate.day += 1;
}
aCount.value = results.length;
return results;
]]></body>
</method>
<!-- public properties and methods -->
<!-- whether to show days outside of the current month -->
<property name="showDaysOutsideMonth">
<getter><![CDATA[
return this.mShowDaysOutsideMonth;
]]></getter>
<setter><![CDATA[
if (this.mShowDaysOutsideMonth != val) {
this.mShowDaysOutsideMonth = val;
this.refresh();
}
return val;
]]></setter>
</property>
<!-- private properties and methods -->
<property name="monthgrid" readonly="true"
onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'monthgrid');"/>
<property name="monthgridrows" readonly="true"
onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'monthgridrows');"/>
<field name="mRefreshQueue">[]</field>
<field name="mRefreshPending">null</field>
<method name="popRefreshQueue">
<body><![CDATA[
var pendingRefresh = this.mRefreshPending;
if (pendingRefresh) {
if (pendingRefresh instanceof Components.interfaces.calIOperation) {
this.mRefreshPending = null;
pendingRefresh.cancel(null);
} else {
if(this.mRefreshQueue.length > 0) {
this.relayout();
}
return;
}
}
var refreshJob = this.mRefreshQueue.pop();
if (!refreshJob) {
return;
}
if (!this.startDate || !this.endDate)
return;
this.relayout();
if (!this.mCalendar)
return;
var filter = this.mCalendar.ITEM_FILTER_CLASS_OCCURRENCES;
if (this.showCompleted) {
filter |= this.mCalendar.ITEM_FILTER_COMPLETED_ALL;
} else {
filter |= this.mCalendar.ITEM_FILTER_COMPLETED_NO;
}
if (this.mTasksInView)
filter |= this.mCalendar.ITEM_FILTER_TYPE_ALL;
else
filter |= this.mCalendar.ITEM_FILTER_TYPE_EVENT;
this.mRefreshPending = true;
pendingRefresh = this.mCalendar.getItems(filter,
0,
this.startDate,
this.queryEndDate,
this.mOperationListener);
if (pendingRefresh && pendingRefresh.isPending) { // support for calIOperation
this.mRefreshPending = pendingRefresh;
}
]]></body>
</method>
<method name="refresh">
<body><![CDATA[
var refreshJob = {};
this.mRefreshQueue.push(refreshJob);
this.popRefreshQueue();
]]></body>
</method>
<method name="relayout">
<body><![CDATA[
function createXULElement(el) {
return document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", el);
}
// Adjust headers based on the starting day of the week, if necessary
var headerbox = document.getAnonymousElementByAttribute(this, "anonid", "headerbox");
if (headerbox.firstChild.index != this.mWeekStartOffset) {
var i = 0;
for each(header in headerbox.childNodes) {
header.index = (i + this.mWeekStartOffset) % 7;
i++;
}
}
if (this.mSelectedItems.length) {
this.mSelectedItems = [];
}
// Clear out the old selection, since it won't be valid after relayout
if (this.mSelectedDayBox) {
this.mSelectedDayBox.box.selected = false;
}
if (!this.mStartDate || !this.mEndDate)
throw NS_ERROR_FAILURE;
// Days that are not in the main month on display are displayed with
// a gray background. Unless the month actually starts on a Sunday,
// this means that mStartDate.month is 1 month less than the main month
var mainMonth = this.mStartDate.month;
if (this.mStartDate.day != 1) {
mainMonth++;
mainMonth = mainMonth % 12;
}
var dateBoxes = [];
var today = this.today();
// This gets set to true, telling us to collapse the rest of the rows
var finished = false;
var dateList = this.getDateList({})
var rows = this.monthgridrows.childNodes;
// Iterate through each monthgridrow and set up the day-boxes that
// are its child nodes. Remember, childNodes is not a normal array,
// so don't use the in operator if you don't want extra properties
// coming out.
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
// If we've already assigned all of the day-boxes that we need, just
// collapse the rest of the rows, otherwise expand them if needed.
if (finished) {
row.setAttribute("collapsed", true);
continue;
} else {
row.removeAttribute("collapsed");
}
for (var j = 0; j < row.childNodes.length; j++) {
var daybox = row.childNodes[j];
var date = dateList[dateBoxes.length];
daybox.setAttribute("context", this.getAttribute("context"));
daybox.setAttribute("item-context", this.getAttribute("item-context") || this.getAttribute("context"));
// Set the box-class depending on if this box displays a day in
// the month being currently shown or not.
var boxClass;
if (this.showFullMonth) {
boxClass = "calendar-month-day-box-" +
(mainMonth == date.month ? "current-month" : "other-month");
} else {
boxClass = "calendar-month-day-box-current-month";
}
function matchesDayOff(dayOffNum) { return dayOffNum == date.weekday; }
if (this.mDaysOffArray.some(matchesDayOff)) {
boxClass = "calendar-month-day-box-day-off " + boxClass;
}
// Set up date relations
switch (date.compare(today)) {
case -1:
daybox.setAttribute("relation", "past");
break;
case 0:
daybox.setAttribute("relation", "today");
break;
case 1:
daybox.setAttribute("relation", "future");
break;
}
daybox.setAttribute("class", boxClass);
daybox.setDate(date);
if (date.day == 1 || date.day == date.endOfMonth.day) {
daybox.showMonthLabel = true;
} else {
daybox.showMonthLabel = false;
}
daybox.monthView = this;
// add the box and its data to our stored array
var boxdata = {
date: date,
row: row,
box: daybox
};
dateBoxes.push(boxdata);
// If we've now assigned all of our dates, set this to true so we
// know we can just collapse the rest of the rows.
if (dateBoxes.length == dateList.length) {
finished = true;
}
}
}
// If we're not showing a full month, then add a few extra labels to
// help the user orient themselves in the view.
if (!this.mShowFullMonth) {
dateBoxes[0].box.showMonthLabel = true;
dateBoxes[dateBoxes.length-1].box.showMonthLabel = true;
}
// Store these, so that we can access them later
this.mDateBoxes = dateBoxes;
this.hideDaysOff();
// Highlight today, if it's in the range of the view
if (today.compare(dateList[0]) != -1 &&
today.compare(dateList[dateList.length-1]) != 1) {
this.findBoxForDate(today).box.setAttribute("today", "true");
}
]]></body>
</method>
<method name="hideDaysOff">
<body><![CDATA[
var columns = document.getAnonymousElementByAttribute(this, "anonid", "monthgridcolumns").childNodes;
var headerkids = document.getAnonymousElementByAttribute(this, "anonid", "headerbox").childNodes;
for (var i in columns) {
var dayForColumn = (Number(i) + this.mWeekStartOffset) % 7;
var dayOff = (this.mDaysOffArray.indexOf(dayForColumn) != -1);
columns[i].collapsed = dayOff && !this.mDisplayDaysOff;
headerkids[i].collapsed = dayOff && !this.mDisplayDaysOff;
}
]]></body>
</method>
<method name="findBoxForDate">
<parameter name="aDate"/>
<body><![CDATA[
for each (box in this.mDateBoxes) {
if (box.date.compare(aDate) == 0)
return box;
}
return null;
]]></body>
</method>
<method name="findBoxesForItem">
<parameter name="aItem"/>
<body><![CDATA[
var targetDate = null;
var finishDate = null;
var boxes = new Array();
// All our boxes are in default tz, so we need these times in them too.
if (isEvent(aItem)) {
targetDate = aItem.startDate.getInTimezone(this.mTimezone);
finishDate = aItem.endDate.getInTimezone(this.mTimezone);
} else if (isToDo(aItem)) {
if (aItem.entryDate) {
targetDate = aItem.entryDate.getInTimezone(this.mTimezone);
if (aItem.dueDate) {
finishDate = aItem.dueDate.getInTimezone(this.mTimezone);
}
}
}
if (!targetDate)
return boxes;
if (!finishDate) {
var maybeBox = this.findBoxForDate(targetDate);
if (maybeBox) {
boxes.push(maybeBox);
}
return boxes;
}
if (!targetDate.isDate) {
// Reset the time to 00:00, so that we really get all the boxes
targetDate.hour = 0;
targetDate.minute = 0;
targetDate.second = 0;
}
if (targetDate.compare(finishDate) == 0) {
// Zero length events are silly, but we have to handle them
var box = this.findBoxForDate(targetDate);
if (box) {
boxes.push(box);
}
}
while (targetDate.compare(finishDate) == -1) {
var box = this.findBoxForDate(targetDate);
// This might not exist, if the event spans the view start or end
if (box) {
boxes.push(box);
}
targetDate.day += 1;
}
return boxes;
]]></body>
</method>
<method name="doAddItem">
<parameter name="aItem"/>
<body><![CDATA[
var boxes = this.findBoxesForItem(aItem);
if (!boxes.length)
return;
for each (box in boxes) {
box.box.addItem(aItem);
}
]]></body>
</method>
<method name="doDeleteItem">
<parameter name="aItem"/>
<body><![CDATA[
var boxes = this.findBoxesForItem(aItem);
if (!boxes.length)
return;
function isNotItem(a) {
return (a.hashId != aItem.hashId);
}
var oldLength = this.mSelectedItems.length;
this.mSelectedItems = this.mSelectedItems.filter(isNotItem);
for each (box in boxes) {
box.box.deleteItem(aItem);
}
// If a deleted event was selected, we need to announce that the
// selection changed.
if (oldLength != this.mSelectedItems.length) {
this.fireEvent("itemselect", this.mSelectedItems);
}
]]></body>
</method>
<method name="today">
<body><![CDATA[
var date = createDateTime();
date.jsDate = new Date();
date = date.getInTimezone(this.mTimezone);
date.isDate = true;
return date;
]]></body>
</method>
<!-- our private observers and listeners -->
<field name="mOperationListener"><![CDATA[
({
calView: this,
QueryInterface: function (aIID) {
if (!aIID.equals(Components.interfaces.calIOperationListener) &&
!aIID.equals(Components.interfaces.nsISupports)) {
throw Components.results.NS_ERROR_NO_INTERFACE;
}
return this;
},
onOperationComplete: function(aCalendar, aStatus, aOperationType, aId, aDetail) {
// Fire viewloaded event
this.calView.fireEvent('viewloaded', aOperationType);
// signal that the current operation finished.
this.calView.mRefreshPending = null;
// immediately start the next job on the queue.
this.calView.popRefreshQueue();
},
onGetResult: function(aCalendar, aStatus, aItemType, aDetail, aCount, aItems) {
if (!Components.isSuccessCode(aStatus))
return;
for each (var item in aItems) {
this.calView.doAddItem(item);
}
}
})
]]></field>
<field name="mObserver"><![CDATA[
// the calIObserver, calICompositeObserver, calIAlarmServiceObserver
({
calView: this,
mBatchCount: 0,
QueryInterface: function (aIID) {
if (!aIID.equals(Components.interfaces.calIObserver) &&
!aIID.equals(Components.interfaces.calICompositeObserver) &&
!aIID.equals(Components.interfaces.calIAlarmServiceObserver) &&
!aIID.equals(Components.interfaces.nsISupports)) {
throw Components.results.NS_ERROR_NO_INTERFACE;
}
return this;
},
onStartBatch: function() {
this.mBatchCount++;
},
onEndBatch: function() {
this.mBatchCount--;
if (this.mBatchCount == 0) {
this.calView.refresh();
}
},
onLoad: function() {
this.calView.refresh();
},
onAddItem: function (aItem) {
if (this.mBatchCount) {
return;
}
if (isToDo(aItem)) {
if (!this.calView.mTasksInView) {
return;
}
if (aItem.isCompleted && !this.calView.mShowCompleted) {
return;
}
}
var occs = aItem.getOccurrencesBetween(this.calView.startDate,
this.calView.queryEndDate,
{});
for each (var occ in occs)
this.calView.doAddItem(occ);
},
onModifyItem: function (aNewItem, aOldItem) {
if (this.mBatchCount) {
return;
}
if (isToDo(aNewItem) && isToDo(aOldItem) &&
!this.calView.mTasksInView) {
return;
}
var occs;
occs = aOldItem.getOccurrencesBetween(this.calView.startDate,
this.calView.queryEndDate,
{});
for each (var occ in occs)
this.calView.doDeleteItem(occ);
if (isToDo(aNewItem)) {
if (!this.calView.mTasksInView) {
return;
}
if (aNewItem.isCompleted && !this.calView.mShowCompleted) {
return;
}
}
occs = aNewItem.getOccurrencesBetween(this.calView.startDate,
this.calView.queryEndDate,
{});
for each (var occ in occs)
this.calView.doAddItem(occ);
},
onDeleteItem: function (aItem) {
if (this.mBatchCount) {
return;
}
if (isToDo(aItem)) {
if (!this.calView.mTasksInView) {
return;
}
if (aItem.isCompleted && !this.calView.mShowCompleted) {
return;
}
}
var occs = aItem.getOccurrencesBetween(this.calView.startDate,
this.calView.queryEndDate,
{});
for each (var occ in occs) {
this.calView.doDeleteItem(occ);
}
},
onError: function (aErrNo, aMessage) { },
onPropertyChanged: function(aCalendar, aName, aValue, aOldValue) {
if (aName == "suppressAlarms" &&
aCalendar.getProperty("capabilities.alarms.popup.supported") !== false &&
getPrefSafe("calendar.alarms.indicator.show", true)) {
this.calView.refresh();
}
},
onPropertyDeleting: function(aCalendar, aName) {
this.onPropertyChanged(aCalendar, aName, null, null);
},
//
// calIAlarmServiceObserver stuff
//
onAlarm: function onAlarm(aAlarmItem) {
this.calView.flashAlarm(aAlarmItem, false);
},
onRemoveAlarmsByItem: function onRemoveAlarmsByItem(aItem) {
// Stop the flashing for the item.
this.calView.flashAlarm(aItem, true);
},
onRemoveAlarmsByCalendar: function onRemoveAlarmsByCalendar(aCalendar) {
// Stop the flashing for all items of this calendar
for each (var item in this.calView.mFlashingEvents) {
if (item.calendar.id == aCalendar.id) {
this.calView.flashAlarm(item, true);
}
}
},
//
// calICompositeObserver stuff
// XXXvv we can be smarter about how we handle this stuff
//
onCalendarAdded: function (aCalendar) {
this.calView.refresh();
},
onCalendarRemoved: function (aCalendar) {
this.calView.refresh();
},
onDefaultCalendarChanged: function (aNewDefaultCalendar) {
// don't care, for now
}
})
]]></field>
<method name="flashAlarm">
<parameter name="aAlarmItem"/>
<parameter name="aStop"/>
<body><![CDATA[
var showIndicator = getPrefSafe("calendar.alarms.indicator.show", true);
var totaltime = getPrefSafe("calendar.alarms.indicator.totaltime", 3600);
if (!aStop && (!showIndicator || totaltime < 1)) {
// No need to animate if the indicator should not be shown.
return;
}
// Make sure the flashing attribute is set or reset on all visible
// boxes.
var boxes = this.findBoxesForItem(aAlarmItem);
for each (var box in boxes) {
for each (var itemData in box.box.mItemData) {
if (itemData.item.hasSameIds(aAlarmItem)) {
if (aStop) {
itemData.box.removeAttribute("flashing");
} else {
itemData.box.setAttribute("flashing", "true");
}
}
}
}
if (!aStop) {
// Set up a timer to stop the flashing after the total time.
var this_ = this;
this.mFlashingEvents[aAlarmItem.hashId] = aAlarmItem;
setTimeout(function() { this_.flashAlarm(aAlarmItem, true) }, totaltime);
} else {
// We are done flashing, prevent newly created event boxes from flashing.
delete this.mFlashingEvents[aAlarmItem.hashId];
}
]]></body>
</method>
</implementation>
<handlers>
<handler event="keypress"><![CDATA[
const kKE = Components.interfaces.nsIDOMKeyEvent;
if (event.keyCode == kKE.DOM_VK_BACK_SPACE ||
event.keyCode == kKE.DOM_VK_DELETE)
{
if (!this.activeInPlaceEdit && this.mSelectedItems.length && this.controller) {
this.controller.deleteOccurrences(this.mSelectedItems.length,
this.mSelectedItems,
event.ctrlKey,
false);
}
}
]]></handler>
</handlers>
</binding>
</bindings>